home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 11 Learning / 04 Mommersteeg / Tennis / Aibot.cpp next >
Encoding:
C/C++ Source or Header  |  2001-09-23  |  12.0 KB  |  392 lines

  1. //----------------------------------------------------------------------------------------------
  2. // Sequential Prediction Demo: The positioning pattern
  3. // 
  4. // Author:  Fri Mommersteeg
  5. // Date:    10-09-2001
  6. // File:    AiBot.cpp
  7. //----------------------------------------------------------------------------------------------
  8.  
  9. //----------------------------------------------------------------------------------------------
  10. // Include files
  11. //----------------------------------------------------------------------------------------------
  12.  
  13. #include "stdafx.h"
  14. #include "aibot.h"
  15.  
  16. //----------------------------------------------------------------------------------------------
  17. // External declarations
  18. //----------------------------------------------------------------------------------------------
  19.  
  20. extern LPDDS imgRedCross;
  21.  
  22. //----------------------------------------------------------------------------------------------
  23. // PredictorFactory: Produces a predictor
  24. //----------------------------------------------------------------------------------------------
  25.  
  26. CTennisPredictor * CAiBot::PredictorFactory() {
  27.     // create string matching predictor
  28.     CTennisPredictor * predictor = new CTennisPredictor;
  29.  
  30.     // set up string matching predictor
  31.     predictor->Setup(WINDOW_SIZE, 2, MIN_PATTERN_SIZE);
  32.  
  33.     // return predictor
  34.     return predictor;
  35. }
  36.  
  37. //----------------------------------------------------------------------------------------------
  38. // SetupAI(): Sets up the AI and registers the tennis ball
  39. //----------------------------------------------------------------------------------------------
  40.  
  41. void CAiBot::SetupAI(CTennisBall * TennisBall, BOOL bTargeting) {
  42.     pHorizontal = PredictorFactory();
  43.     pVertical   = PredictorFactory();
  44.     
  45.     halfwidth = (field.right-field.left) / 2;
  46.     halfheight = (field.bottom-field.top) / 2;
  47.         
  48.     nBounces = 0;
  49.     this->bTargeting = bTargeting;
  50.  
  51.     ball = TennisBall;
  52.     ball->RegisterObserver(this);
  53. }
  54.  
  55. //----------------------------------------------------------------------------------------------
  56. // EnableTargeting(): Enables or disables the bot targeting
  57. //----------------------------------------------------------------------------------------------
  58.  
  59. void CAiBot::EnableTargeting(BOOL bEnabled) {
  60.     bTargeting = bEnabled;
  61.     if (!bTargeting) {
  62.         rotation = 0;
  63.     }
  64. }
  65.  
  66. //----------------------------------------------------------------------------------------------
  67. // Notify(): Observer notification handler (here the bot receives notifications from the ball)
  68. //----------------------------------------------------------------------------------------------
  69.  
  70. BOOL CAiBot::Notify(int msg, DWORD param) {
  71.     if (msg == HIT_GROUND_EVENT) {
  72.         POINT pt;
  73.         ball->GetPosition(pt);
  74.         if (PtInRect(&field, pt)) {
  75.             nBounces++;    
  76.             
  77.             // is this the first time that the ball bounces in this field?
  78.             if (nBounces == 1) {
  79.                 // generate next elements for sequences
  80.                 int next_horizontal, next_vertical;
  81.                 
  82.                 // update "horizontal" sequence
  83.                 next_horizontal = pt.x > field.left + halfwidth;
  84.                 pHorizontal->Update(next_horizontal);
  85.                 
  86.                 // update "vertical" sequence
  87.                 next_vertical   = pt.y > field.top + halfheight;
  88.                 pVertical->Update(next_vertical);            
  89.             }
  90.         }
  91.     }
  92.     return TRUE;
  93. }
  94.  
  95. //----------------------------------------------------------------------------------------------
  96. // OnBallCollision(): Used to determine a new ball destination; overriden from CPaddle
  97. //----------------------------------------------------------------------------------------------
  98.  
  99. void CAiBot::OnBallCollision() {
  100.     
  101.     int Destination;
  102.  
  103.     // randomly chooce a destination to play the ball to next time
  104.     RandomPredictor.GetPrediction(Destination);
  105.  
  106.     BallDestination.x = field.left + OUTBOX_WIDTH + (Destination * (field.right-field.left-2*OUTBOX_WIDTH)) / PADDLE_ANGLES;
  107.     BallDestination.y = (int)py + direction * (field.bottom-field.top);
  108. }
  109.  
  110. //----------------------------------------------------------------------------------------------
  111. // AdjustPaddleAngle(): Adjust paddle angle so that it is directed towards the ball destination
  112. //----------------------------------------------------------------------------------------------
  113.  
  114. void CAiBot::AdjustPaddleAngle() {    
  115.     float dy;
  116.     float dist;
  117.     
  118.     dy = ((float)BallDestination.y - py);    
  119.     dist = px + (dy*nx) / ny;
  120.  
  121.     if ((int)dist < BallDestination.x) {
  122.         Rotate(-direction*ROTATE_SPEED);
  123.     } else {
  124.         Rotate(direction*ROTATE_SPEED);
  125.     }
  126. }
  127.  
  128. //----------------------------------------------------------------------------------------------
  129. // SeekTarget(): Determines the target position to move the paddle to
  130. //----------------------------------------------------------------------------------------------
  131.  
  132. void CAiBot::SeekTarget() {
  133.  
  134.     if (ball->GetOwner() != OWNER_SERVICE || (ball->GetOwner() == OWNER_SERVICE && bHasService)) {
  135.         int Prediction;
  136.         
  137.         // get ball position
  138.         POINT pt;
  139.         ball->GetPosition(pt);
  140.  
  141.         // if ball is not inside AI field, reset ball bounce count
  142.         if (!PtInRect(&field, pt)) {
  143.             nBounces = 0;
  144.         }
  145.  
  146.         // is this bot the "owner" of the ball? from this we derive the general direction of the ball
  147.         if (ball->GetOwner() != id) {
  148.             if (nBounces == 0 && !(ball->GetOwner() == OWNER_SERVICE && bHasService)) {
  149.                 // interpolate the motion of the ball to where it will hit the ground
  150.                 ball->GetBouncePosition(target);                    
  151.                 heuristic = INTERPOLATION;
  152.             } else {
  153.                 // move straightly towards the ball
  154.                 target = pt;
  155.                 heuristic = TOWARDSBALL;
  156.             }
  157.         } else {
  158.             heuristic = TOWARDSCENTER;
  159.  
  160.             // perform prediction for horizontal coordinate
  161.             if (pHorizontal->GetPrediction(Prediction)) {
  162.  
  163.                 // get the match size from the predictor
  164.                 int indicator = pHorizontal->GetMaxMatchSize();
  165.  
  166.                 // limit the match size to the window size
  167.                 indicator = (indicator > WINDOW_SIZE) ? WINDOW_SIZE : indicator;
  168.  
  169.                 // convert 0=left,1=right to -1=left,1=right
  170.                 Prediction = Prediction ? 1 : -1;
  171.  
  172.                 // determine position
  173.                 target.x = field.left + halfwidth + (Prediction*indicator*halfwidth)/(2*WINDOW_SIZE);                
  174.                 heuristic = ANTICIPATION;
  175.             } else {
  176.                 target.x = field.left + halfwidth;
  177.             }
  178.  
  179.             // perform prediction for vertical coordinate
  180.             if (pVertical->GetPrediction(Prediction)) {
  181.  
  182.                 // get the match size from the predictor
  183.                 int indicator = pVertical->GetMaxMatchSize();
  184.  
  185.                 // limit the match size to the window size
  186.                 indicator = (indicator > WINDOW_SIZE) ? WINDOW_SIZE : indicator;
  187.  
  188.                 // convert 0=up,1=down to -1=up,1=down
  189.                 Prediction = Prediction ? 1 : -1;
  190.  
  191.                 // determine position... yes, it's a long calculation
  192.                 target.y = field.top + halfheight - direction * halfheight / 2 + (Prediction*indicator*halfheight)/(4*WINDOW_SIZE);                
  193.             } else {
  194.                 target.y = field.top + halfheight - direction * halfheight / 2;
  195.             }        
  196.         }
  197.     } else {
  198.         // do not move during service
  199.         target.x = (int)px;
  200.         target.y = (int)py;
  201.     }
  202. }
  203.  
  204. //----------------------------------------------------------------------------------------------
  205. // AdvanceTarget(): Moves the paddle towards the target position
  206. //----------------------------------------------------------------------------------------------
  207.  
  208. void CAiBot::AdvanceTarget() {
  209.     // we use an extremely simple method to get to the target position
  210.  
  211.     long dx, dy;
  212.     dx = target.x - (int)px;
  213.     dy = target.y - (int)py;
  214.  
  215.     if (abs(dx)>abs(dy)) {
  216.         if (dx<0) {            
  217.             // if we can't move to the horizontal, then move vertical
  218.             if (px<=field.left) {
  219.                 if (dy<0) {
  220.                     Up();
  221.                 } else {
  222.                     Down();
  223.                 }
  224.             } else {
  225.                 Left();
  226.             }
  227.         } else {
  228.             // if we can't move to the horizontal, then move vertical
  229.             if (px>=field.right) {
  230.                 if (dy<0) {
  231.                     Up();
  232.                 } else {
  233.                     Down();
  234.                 }
  235.             } else {
  236.                 Right();
  237.             }
  238.         }
  239.     } else {
  240.         if (dy<0) {
  241.             // if we can't move to the vertical, then move horizontal
  242.             if (py<=field.top) {
  243.                 if (dx<0) {
  244.                     Left();
  245.                 } else {
  246.                     Right();
  247.                 }
  248.             } else {
  249.                 Up();
  250.             }            
  251.         } else if (dy>0) {
  252.             // if we can't move to the vertical, then move horizontal
  253.             if (py>=field.bottom) {
  254.                 if (dx<0) {
  255.                     Left();
  256.                 } else {
  257.                     Right();
  258.                 }
  259.             } else {
  260.                 Down();
  261.             }            
  262.         }
  263.     }
  264. }
  265.  
  266. //----------------------------------------------------------------------------------------------
  267. // Update(): Updates the status of the bot
  268. //----------------------------------------------------------------------------------------------
  269.  
  270. void CAiBot::Update() {
  271.     SeekTarget();
  272.     AdvanceTarget();
  273.  
  274.     if (bTargeting) {
  275.         AdjustPaddleAngle();
  276.     }
  277.  
  278.     CPaddle::Update();
  279. }
  280.  
  281. //----------------------------------------------------------------------------------------------
  282. // PaintSequence(): Displays sequence information on the screen
  283. //----------------------------------------------------------------------------------------------
  284.  
  285. void CAiBot::PaintSequence(HDC dc, int x, int y, char * szA, char * szB, CTennisPredictor * pPredictor, int Width) {
  286.     int nWindowSize;
  287.     int nPatternSize;
  288.     int nPatternStart;
  289.     int nPatternEnd;
  290.     int element;
  291.     int i;
  292.  
  293.     nWindowSize = pPredictor->GetWindowSize();
  294.     nPatternSize = pPredictor->GetMaxMatchSize();
  295.     nPatternEnd = pPredictor->GetMaxMatchPosition()+1;
  296.     nPatternStart = nPatternEnd-nPatternSize;
  297.     for (i = 0; i<nWindowSize; i++) {        
  298.         if (i >= nPatternStart && i < nPatternEnd) {
  299.             SetTextColor(dc, RGB(255, 255, 0));
  300.         } else if (i == nPatternEnd && nPatternSize >= MIN_PATTERN_SIZE) {
  301.             SetTextColor(dc, RGB(0, 255, 0));        
  302.         } else {
  303.             SetTextColor(dc, RGB(255,255,255));
  304.         }
  305.         element = pPredictor->GetWindowEntry(i);
  306.         if (element == 0) {
  307.             TextOut(dc, x+i*Width,y, szA, 1);
  308.         } else {
  309.             TextOut(dc, x+i*Width,y, szB, 1);
  310.         }
  311.     }
  312. }
  313.  
  314. //----------------------------------------------------------------------------------------------
  315. // ShowStatistics(): Shows AI statistics on the screen
  316. //----------------------------------------------------------------------------------------------
  317.  
  318. void CAiBot::ShowStatistics(HDC dc) {
  319.     SetBkColor(dc, RGB(0,0,0));
  320.     SetTextColor(dc, RGB(255,255,255));
  321.     TextOut(dc, 10, 10, "Horizontal Predictor:    ", 25);
  322.     TextOut(dc, 10, 30, "Vertical Predictor:      ", 25);
  323.  
  324.     PaintSequence(dc, 200, 10, "L", "R", pHorizontal);
  325.     PaintSequence(dc, 200, 30, "A", "B", pVertical);
  326.     
  327.     SetTextColor(dc, RGB(255,255,255));
  328.     TextOut(dc, 10, 80, "Positioning strategy: ", 21);
  329.  
  330.     switch (heuristic) {
  331.     case ANTICIPATION:
  332.         TextOut(dc, 10, 100, "ANTICIPATION   ", 15);
  333.         break;
  334.     case INTERPOLATION:
  335.         TextOut(dc, 10, 100, "INTERPOLATION  ", 15);
  336.         break;
  337.     case TOWARDSBALL:
  338.         TextOut(dc, 10, 100, "TOWARDS BALL   ", 15);
  339.         break;
  340.     case TOWARDSCENTER:
  341.         TextOut(dc, 10, 100, "TOWARDS CENTER ", 15);    
  342.         break;
  343.     }
  344. }
  345.  
  346. //----------------------------------------------------------------------------------------------
  347. // Paint(): Paints the AI paddle; overriden from CPaddle
  348. //----------------------------------------------------------------------------------------------
  349.  
  350. void CAiBot::Paint(LPDDS lpdds) {
  351.     
  352.     // display a red cross in case the bot uses anticipation
  353.     if (heuristic == ANTICIPATION) {
  354.         RECT source, dest;
  355.         GetSurfaceSize(imgRedCross, &source);
  356.         CopyRect(&dest, &source);
  357.         OffsetRect(&dest, target.x - CROSS_WIDTH / 2, target.y - CROSS_HEIGHT / 2);
  358.         Blit(imgRedCross, &source, lpddss, &dest, 0);
  359.     }
  360.  
  361.     // call parent paint 
  362.     CPaddle::Paint(lpdds);
  363.  
  364.     // show AI statistics
  365.     HDC dc;
  366.     lpdds->GetDC(&dc);
  367.     ShowStatistics(dc);        
  368.     lpdds->ReleaseDC(dc);
  369. }
  370.  
  371. //----------------------------------------------------------------------------------------------
  372. // ResetForService(): Resets the AI bot for the service
  373. //----------------------------------------------------------------------------------------------
  374.  
  375. void CAiBot::ResetForService(BOOL HasService) {
  376.     // call default implementation
  377.     CPaddle::ResetForService(HasService);
  378.  
  379.     // reset predictors
  380.     if (pHorizontal != NULL) {
  381.         pHorizontal->Reset();
  382.     }
  383.     if (pVertical != NULL) {
  384.         pVertical->Reset();
  385.     }
  386.  
  387.     bHasService = HasService;
  388.     heuristic = TOWARDSCENTER;
  389.  
  390.     OnBallCollision();
  391. }
  392.